// Copyright Epic Games, Inc. All Rights Reserved.


#include "AnimNotify_LyraContextEffects.h"
#include "Feedback/ContextEffects/LyraContextEffectsLibrary.h"
#include "LyraContextEffectsInterface.h"
#include "Components/SkeletalMeshComponent.h"
#include "Engine/World.h"
#include "LyraContextEffectsSubsystem.h"
#include "NiagaraFunctionLibrary.h"
#include "Kismet/GameplayStatics.h"
#include "NiagaraSystem.h"

#include UE_INLINE_GENERATED_CPP_BY_NAME(AnimNotify_LyraContextEffects)



UAnimNotify_LyraContextEffects::UAnimNotify_LyraContextEffects()
{
	// 构造函数
}

void UAnimNotify_LyraContextEffects::PostLoad()
{
	Super::PostLoad();
	// 后加载处理
}

#if WITH_EDITOR
void UAnimNotify_LyraContextEffects::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
	Super::PostEditChangeProperty(PropertyChangedEvent);
	// 后编辑更改属性处理
}
#endif

/**
 * 获取通知名称的实现
 */
FString UAnimNotify_LyraContextEffects::GetNotifyName_Implementation() const
{
	// 如果效果标签有效，将字符串名称传递给通知名称
	if (Effect.IsValid())
	{
		return Effect.ToString();
	}

	return Super::GetNotifyName_Implementation();
}

/**
 * 通知函数，在动画播放时触发
 */
void UAnimNotify_LyraContextEffects::Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation,
	const FAnimNotifyEventReference& EventReference)
{
	Super::Notify(MeshComp, Animation, EventReference); // 调用父类通知

	if (MeshComp) // 检查网格组件是否有效
	{
		// 确保网格组件和拥有者Actor都有效
		if (AActor* OwningActor = MeshComp->GetOwner())
		{
			// 准备追踪数据
			bool bHitSuccess = false; // 命中成功标志
			FHitResult HitResult; // 命中结果
			FCollisionQueryParams QueryParams; // 碰撞查询参数

			if (TraceProperties.bIgnoreActor) // 如果忽略Actor
			{
				QueryParams.AddIgnoredActor(OwningActor); // 添加忽略的Actor
			}

			QueryParams.bReturnPhysicalMaterial = true; // 返回物理材质

			if (bPerformTrace) // 如果需要执行追踪
			{
				// 如果需要追踪，设置起始位置为附加位置
				FVector TraceStart = bAttached ? MeshComp->GetSocketLocation(SocketName) : MeshComp->GetComponentLocation();

				// 确保世界有效
				if (UWorld* World = OwningActor->GetWorld())
				{
					// 调用线性追踪，传入相关属性
					bHitSuccess = World->LineTraceSingleByChannel(HitResult, TraceStart, (TraceStart + TraceProperties.EndTraceLocationOffset),
						TraceProperties.TraceChannel, QueryParams, FCollisionResponseParams::DefaultResponseParam);
				}
			}

			// 预先准备上下文
			FGameplayTagContainer Contexts; // 上下文容器

			// 设置实现上下文效果接口的对象数组
			TArray<UObject*> LyraContextEffectImplementingObjects; // 实现上下文效果接口的对象数组

			// 确定拥有者Actor是否是实现上下文效果接口的对象之一
			if (OwningActor->Implements<ULyraContextEffectsInterface>())
			{
				// 如果是，将其添加到数组
				LyraContextEffectImplementingObjects.Add(OwningActor);
			}

			// 遍历拥有者Actor的组件，确定是否有任何组件实现了上下文效果接口
			for (const auto Component : OwningActor->GetComponents())
			{
				if (Component) // 检查组件是否有效
				{
					// 如果组件实现了上下文效果接口，将其添加到列表
					if (Component->Implements<ULyraContextEffectsInterface>())
					{
						LyraContextEffectImplementingObjects.Add(Component);
					}
				}
			}

			// 遍历所有实现上下文效果接口的对象
			for (UObject* LyraContextEffectImplementingObject : LyraContextEffectImplementingObjects)
			{
				if (LyraContextEffectImplementingObject) // 检查对象是否仍然有效
				{
					// 如果对象仍然有效，执行其上的AnimMotionEffect事件，传入相关数据
					ILyraContextEffectsInterface::Execute_AnimMotionEffect(LyraContextEffectImplementingObject,
						(bAttached ? SocketName : FName("None")),
						Effect, MeshComp, LocationOffset, RotationOffset,
						Animation, bHitSuccess, HitResult, Contexts, VFXProperties.Scale,
						AudioProperties.VolumeMultiplier, AudioProperties.PitchMultiplier);
				}
			}

#if WITH_EDITORONLY_DATA
			// 这是用于动画编辑器预览，解构了接口和子系统进行的调用
			if (bPreviewInEditor) // 如果在编辑器中预览
			{
				UWorld* World = OwningActor->GetWorld(); // 获取世界

				// 获取世界，确保它是编辑器预览世界
				if (World && World->WorldType == EWorldType::EditorPreview)
				{
					// 如果需要，添加上下文预览
					Contexts.AppendTags(PreviewProperties.PreviewContexts); // 追加预览上下文

					// 将给定的表面类型转换为上下文，并为此预览添加到上下文
					if (PreviewProperties.bPreviewPhysicalSurfaceAsContext) // 如果预览物理表面作为上下文
					{
						TEnumAsByte<EPhysicalSurface> PhysicalSurfaceType = PreviewProperties.PreviewPhysicalSurface; // 物理表面类型

						if (const ULyraContextEffectsSettings* LyraContextEffectsSettings = GetDefault<ULyraContextEffectsSettings>()) // 获取上下文效果设置
						{
							if (const FGameplayTag* SurfaceContextPtr = LyraContextEffectsSettings->SurfaceTypeToContextMap.Find(PhysicalSurfaceType)) // 查找表面上下文
							{
								FGameplayTag SurfaceContext = *SurfaceContextPtr; // 表面上下文

								Contexts.AddTag(SurfaceContext); // 添加上下文标签
							}
						}
					}

					// 库是软引用的，所以你现在需要尝试加载它们
					// TODO 异步资源加载
					if (UObject* EffectsLibrariesObj = PreviewProperties.PreviewContextEffectsLibrary.TryLoad()) // 尝试加载预览上下文效果库
					{
						// 检查它是否确实是ULyraContextEffectLibrary类型
						if (ULyraContextEffectsLibrary* EffectLibrary = Cast<ULyraContextEffectsLibrary>(EffectsLibrariesObj)) // 转换为上下文效果库
						{
							// 准备声音和Niagara系统数组
							TArray<USoundBase*> TotalSounds; // 总声音数组
							TArray<UNiagaraSystem*> TotalNiagaraSystems; // 总Niagara系统数组

							// 尝试加载效果库内容（将在效果库资产上的瞬态数据中缓存）
							EffectLibrary->LoadEffects(); // 加载效果

							// 如果效果库有效且标记为已加载，从中获取效果
							if (EffectLibrary && EffectLibrary->GetContextEffectsLibraryLoadState() == EContextEffectsLibraryLoadState::Loaded) // 检查加载状态
							{
								// 准备本地数组
								TArray<USoundBase*> Sounds; // 声音数组
								TArray<UNiagaraSystem*> NiagaraSystems; // Niagara系统数组

								// 获取效果
								EffectLibrary->GetEffects(Effect, Contexts, Sounds, NiagaraSystems); // 获取效果

								// 追加到累积数组
								TotalSounds.Append(Sounds); // 追加声音
								TotalNiagaraSystems.Append(NiagaraSystems); // 追加Niagara系统
							}

							// 遍历声音并调用生成附加声音，传入相关数据
							for (USoundBase* Sound : TotalSounds) // 遍历总声音
							{
								UGameplayStatics::SpawnSoundAttached(Sound, MeshComp, (bAttached ? SocketName : FName("None")), LocationOffset, RotationOffset, EAttachLocation::KeepRelativeOffset,
									false, AudioProperties.VolumeMultiplier, AudioProperties.PitchMultiplier, 0.0f, nullptr, nullptr, true); // 生成附加声音
							}

							// 遍历Niagara系统并调用生成附加系统，传入相关数据
							for (UNiagaraSystem* NiagaraSystem : TotalNiagaraSystems) // 遍历总Niagara系统
							{
								UNiagaraFunctionLibrary::SpawnSystemAttached(NiagaraSystem, MeshComp, (bAttached ? SocketName : FName("None")), LocationOffset,
									RotationOffset, VFXProperties.Scale, EAttachLocation::KeepRelativeOffset, true, ENCPoolMethod::None, true, true); // 生成附加系统
							}
						}
					}
						
				}
			}
#endif

		}
	}
}

#if WITH_EDITOR
void UAnimNotify_LyraContextEffects::ValidateAssociatedAssets()
{
	Super::ValidateAssociatedAssets();
	// 验证关联资产
}

/**
 * 设置参数
 */
void UAnimNotify_LyraContextEffects::SetParameters(FGameplayTag EffectIn, FVector LocationOffsetIn, FRotator RotationOffsetIn,
	FLyraContextEffectAnimNotifyVFXSettings VFXPropertiesIn, FLyraContextEffectAnimNotifyAudioSettings AudioPropertiesIn,
	bool bAttachedIn, FName SocketNameIn, bool bPerformTraceIn, FLyraContextEffectAnimNotifyTraceSettings TracePropertiesIn)
{
	Effect = EffectIn; // 设置效果
	LocationOffset = LocationOffsetIn; // 设置位置偏移
	RotationOffset = RotationOffsetIn; // 设置旋转偏移
	VFXProperties.Scale = VFXPropertiesIn.Scale; // 设置VFX缩放
	AudioProperties.PitchMultiplier = AudioPropertiesIn.PitchMultiplier; // 设置音频音高倍数
	AudioProperties.VolumeMultiplier = AudioPropertiesIn.VolumeMultiplier; // 设置音频音量倍数
	bAttached = bAttachedIn; // 设置是否附加
	SocketName = SocketNameIn; // 设置插槽名称
	bPerformTrace = bPerformTraceIn; // 设置是否执行追踪
	TraceProperties.EndTraceLocationOffset = TracePropertiesIn.EndTraceLocationOffset; // 设置追踪结束位置偏移
	TraceProperties.TraceChannel = TracePropertiesIn.TraceChannel; // 设置追踪通道
	TraceProperties.bIgnoreActor = TracePropertiesIn.bIgnoreActor; // 设置是否忽略Actor

}
#endif